package oli.plugmgr.core
{
	import de.polygonal.ds.Iterator;
	import de.polygonal.ds.SLinkedList;
	
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.system.LoaderContext;
	import flash.system.SecurityDomain;
	
	import oli.plugmgr.errors.PlugLoadError;
	import oli.plugmgr.errors.PlugServiceLoadError;
	import oli.plugpack.core.IPlugInActivator;
	import oli.plugpack.core.PlugIn;
	import oli.plugpack.errors.PlugError;
	import oli.plugpack.events.PlugLoadEvent;
	import oli.plugpack.meta.Info;
	import oli.plugpack.services.PlugService;
	
	public class PlugLoadQue extends EventDispatcher
	{
		private var loader:Loader;
		private var context:LoaderContext;
	//	private var plugDomain:ApplicationDomain;
		private var r:URLRequest;
		private var currentPlugName:String;
		
		private var plugPaths:SLinkedList;
		private var plugins:Vector.<PlugIn>;
		private var metas:Vector.<XML>;
		
		private var index:uint = 0;
		
		private var progress:PlugLoadEvent;
		
		public function PlugLoadQue()
		{
			super(null);
		}
		public function load(plugPaths:SLinkedList):void
		{
			this.plugPaths = plugPaths;
			loader = new Loader();
		//	plugDomain = new ApplicationDomain(loader.contentLoaderInfo.applicationDomain);
			
			
			//FIXME If plugins are on different Server than the PluginManager the LoaderContext needs to
			// set the SecurityDomain to the current Domain
			if(PlugManager.SECURITY_DOMAIN == 'different')
			{
				context = new LoaderContext(false,null,SecurityDomain.currentDomain);
			}
			else if(PlugManager.SECURITY_DOMAIN == 'same')
			{
				
				//context = new LoaderContext(false,plugDomain,null);
				context = null;
			}
			else
			{
				throw new PlugError("Please check the 'securityDomain' setting in the 'plugmngr.settings' file. \n" + 
						PlugManager.SECURITY_DOMAIN+" is not a valid entry. \n");
			}
			
			registerLoaderEventListeners(loader.contentLoaderInfo);
			r = new URLRequest();
			r.method = URLRequestMethod.POST;
			plugins = new Vector.<PlugIn>(plugPaths.size,true);
			metas = new Vector.<XML>(plugPaths.size,true);
			//trace(plugins);
		
			progress = new PlugLoadEvent(plugins,PlugLoadEvent.PROGRESS);
			dispatchEvent(progress);
			
			loadNextPlug();
		}
		private function loadNextPlug():void
		{
			var it:Iterator = plugPaths.getIterator();
			
			if(it.hasNext())
			{
				//trace(plugPaths);
				var plugPath:String = plugPaths.removeHead();
				//trace(plugPath);
				r.url = PlugManager.PLUGINS_PATH + plugPath;
				currentPlugName = plugPath.replace(".swf","");
				//trace(currentPlugName);
				
				
				loader.load(r,context);
			}
			else
			{
				// list is empty... clean up garbage and dispatch master complete event
				dispatchEvent(progress);
				dispatchEvent(new PlugLoadEvent(plugins,PlugLoadEvent.ALL_COMPLETE));
				unRegisterLoaderEventListeners(loader.contentLoaderInfo);
				loader = null;
				context = null;
				r = null;
				plugPaths = null;
				plugins = null;
				metas = null;
				trace("noNext");
			}
		}
		private function loaderHandler(e:Event):void
		{
			
			if (e.type == Event.COMPLETE)
			{
				
				
				var l:LoaderInfo = e.target as LoaderInfo;
				
				try
				{
					var Plug:Class = l.applicationDomain.getDefinition(currentPlugName) as Class;
					//var Plug:Class = plugDomain.getDefinition(currentPlugName) as Class;
					var META_INF:Class = l.applicationDomain.getDefinition("META_INF") as Class;
					//var META_INF:Class = plugDomain.getDefinition("META_INF") as Class;
					
					// reset plugDomain
					
					var plug:IPlugInActivator = new Plug();
					var meta:Info = new META_INF();
				}
				catch(e:Error)
				{
					var origErr:String = e.getStackTrace();
					
					throw new PlugLoadError(currentPlugName);
					
					trace(origErr);
				}
				
				
				var metaInfo:XML = meta.getInfo();	
				var services:XMLList = metaInfo.services;				
			//	trace(services, services.length());
				metaInfo.id = currentPlugName;
				var servVec:Vector.<PlugService> = new Vector.<PlugService>(services.length(),true);
				var k:uint = 0;
				//trace(services);
				for each(var service:XML in services.service)
				{
					//trace(services,service.@fullyQualifiedName);
					
					var serviceID:String = service.@fullyQualifiedName;
					
					try
					{
						trace(l.applicationDomain.hasDefinition("oli.my.plug.Plug2Implement"),
						l.applicationDomain.hasDefinition("META_INF"), 
						l.applicationDomain.hasDefinition("oli.my.service.MyService2"), 
						l.applicationDomain.parentDomain);
						var Service:Class = l.applicationDomain.getDefinition(serviceID) as Class;
						//trace(serviceID, service.@fullyQualifiedName);
					}
					catch (e:Error)
					{
						var stack:String = e.getStackTrace();
						throw new PlugServiceLoadError(currentPlugName, serviceID);
						
					}
					
					var plugService:PlugService = new PlugService(Service,serviceID ,service.@description);
					
				//	trace(plugService.description);
					
					servVec[k] = plugService;
					k++;
				}
				
				var plugin:PlugIn = new PlugIn(plug, metaInfo, servVec);
			//	trace(plugin, plugin.name, plugin.services);
				//trace(meta.getInfo());
				plugins[index] = plugin;
				
				index++;
				loadNextPlug();
			}
			if (e.type == IOErrorEvent.IO_ERROR)
			{
				trace(e);
				throw new PlugError("The plugin '"+currentPlugName+"' could not be loaded. \n" + 
						"Check its existence. \n"+e+"\n");
			}
			if (e.type == SecurityErrorEvent.SECURITY_ERROR)
			{
				trace(e);
				var se:SecurityErrorEvent = e as SecurityErrorEvent;
				throw new SecurityError(se.text);
			}
			if(e.type == ProgressEvent.PROGRESS)
			{
				var pe:ProgressEvent = e as ProgressEvent;
				
				progress.bytesLoaded += pe.bytesLoaded;
				dispatchEvent(progress);
				//trace(e.target, e, progress);
			}
		}
		private function registerLoaderEventListeners(i:IEventDispatcher):void
		{
			i.addEventListener(Event.COMPLETE, loaderHandler, false, 0, false);
			i.addEventListener(IOErrorEvent.IO_ERROR, loaderHandler, false, 0, false);
			i.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loaderHandler, false, 0, false);
			i.addEventListener(ProgressEvent.PROGRESS, loaderHandler, false, 0, false);
		}
		private function unRegisterLoaderEventListeners(i:IEventDispatcher):void
		{
			i.removeEventListener(Event.COMPLETE, loaderHandler);
			i.removeEventListener(IOErrorEvent.IO_ERROR, loaderHandler);
			i.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, loaderHandler);
			i.removeEventListener(ProgressEvent.PROGRESS, loaderHandler);
		}
	}
}